package com.alin.flutter_ble_ota

import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothDevice
import android.bluetooth.BluetoothManager
import android.content.Context
import android.os.Handler
import android.os.Looper
import android.util.Log
import androidx.annotation.NonNull
import com.alin.flutter_ble_ota.tool.bluetooth.BluetoothHelper
import com.alin.flutter_ble_ota.tool.bluetooth.OnBTEventCallback
import com.alin.flutter_ble_ota.tool.ota.OTAManager
import com.alin.flutter_ble_ota.tool.ota.ble.model.BleScanInfo
import com.alin.flutter_ble_ota.util.AppUtil
import com.jieli.jl_bt_ota.constant.StateCode
import com.jieli.jl_bt_ota.interfaces.BtEventCallback
import com.jieli.jl_bt_ota.interfaces.IUpgradeCallback
import com.jieli.jl_bt_ota.model.base.BaseError
import com.jieli.jl_bt_ota.util.JL_Log
import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.plugin.common.EventChannel
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
import io.flutter.plugin.common.MethodChannel.Result

/** FlutterBleOtaPlugin */
class FlutterBleOtaPlugin: FlutterPlugin, MethodCallHandler,EventChannel.StreamHandler {
  /// The MethodChannel that will the communication between Flutter and native Android
  ///
  /// This local reference serves to register the plugin with the Flutter Engine and unregister it
  /// when the Flutter Engine is detached from the Activity
  private lateinit var channel : MethodChannel
  private lateinit var context : Context
  private var mBluetoothManager: BluetoothManager? = null
  private var mBluetoothAdapter: BluetoothAdapter? = null
  private var connectResult: Result? = null
  private var bluetoothHelper : BluetoothHelper? = null
  private var otaManager : OTAManager? = null
  private var isInit = false
  private lateinit var eventChannel: EventChannel
  private var eventSink: EventChannel.EventSink? = null
  private val TAG = "FlutterBleOtaPlugin"

  override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
    Log.e(TAG,"onAttachedToEngine")
    channel = MethodChannel(flutterPluginBinding.binaryMessenger, "flutter_ble_ota")
    context = flutterPluginBinding.applicationContext
    mBluetoothManager = context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
    mBluetoothAdapter = mBluetoothManager!!.adapter
    channel.setMethodCallHandler(this)
    eventChannel = EventChannel(flutterPluginBinding.binaryMessenger, "flutter_ble_ota_event")
    eventChannel.setStreamHandler(this)
  }

  override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {
    Log.e(TAG,"onMethodCall=${call.method}")
    if (call.method == "getPlatformVersion") {
      result.success("Android ${android.os.Build.VERSION.RELEASE}")
    }else if(call.method == "initOTA"){
      if(!isInit){
        bluetoothHelper = BluetoothHelper.getInstance(context)
        otaManager = OTAManager(context)
        otaManager?.registerBluetoothCallback(bluetoothCallback)
        isInit = true
      }
      result.success(true)
    } else if (call.method == "connectDevice"){
      if(isInit){
        val address = call.arguments as String
        Log.e(TAG,"address=$address")
        if(address.isNullOrEmpty()){
          result.success(false)
        }else{
          var device = mBluetoothAdapter!!.getRemoteDevice(address)
          var isOk = bluetoothHelper?.connectDevice(device)?:false
          if(isOk){
            connectResult = result
          }else{
            result.success(false)
          }
        }
      }else{
        result.success(false)
      }
    }else if(call.method == "startOTA"){
      if(!isInit){
        otaError(-1,"not call initOTA")
        return
      }
      val filePath = call.arguments as String
      Log.e(TAG,"filePath=$filePath")
      if(filePath.isNullOrEmpty()){
        otaError(-1,"filePath is isNullOrEmpty")
        return
      }
      val device = bluetoothHelper?.getConnectedDevice()

      if (device == null) {
        otaError(-1,"no connected device")
        return
      }
      if (otaManager?.getDeviceInfo(device) == null) {
        Log.e(TAG,"设备未连接")
        otaError(-1,"no connected device")
        return
      }
      if (otaManager!!.isOTA) {
        JL_Log.w(TAG, "startOTA : Ota is running.")
        otaError(-2,"Ota is running")
        return
      }
      otaManager!!.bluetoothOption.firmwareFilePath = filePath
      otaManager!!.startOTA(object :IUpgradeCallback{
          override fun onStartOTA() {
            Log.e(TAG,"onStartOTA")
            invokeMethodUIThread("otaStart",null)
          }

          override fun onNeedReconnect(addr: String?, isNewReconnectWay: Boolean) {
            Log.e(TAG,"onNeedReconnect addr=$addr  isNewReconnectWay=$isNewReconnectWay")
          }

          override fun onProgress(type: Int, progress: Float) {
            Log.e(TAG,"onProgress  type=$type  progress=$progress")
            sendEventToStream(mapOf("status" to 1,"type" to type,"progress" to progress))
          }

          override fun onStopOTA() {
            Log.e(TAG,"onStopOTA")
            invokeMethodUIThread("otaStop",null)
          }

          override fun onCancelOTA() {
            Log.e(TAG,"onCancelOTA")
            invokeMethodUIThread("otaCancel",null)
          }

          override fun onError(error: BaseError?) {
            Log.e(TAG,"onError=${error.toString()}")
            otaError(error?.code?:-1,error?.message?:"")
          }
        }
      )
    }else if(call.method == "dispose"){
      otaManager?.unregisterBluetoothCallback(bluetoothCallback)
      otaManager?.cancelOTA()
      otaManager?.release()
      otaManager = null
      bluetoothHelper?.destroy()
      bluetoothHelper = null
      isInit = false
      result.success(true)
    }else {
      result.notImplemented()
    }
  }

  override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) {
    channel.setMethodCallHandler(null)
  }

  private fun otaError(code:Int,msg:String){
    sendEventToStream(mapOf("status" to 2,"code" to code,"msg" to msg))
  }
  fun sendEventToStream(data: Any) {
    Handler(Looper.getMainLooper()).post {
      eventSink?.success(data)
    }
  }
  private fun invokeMethodUIThread(name: String, param:Map<String,Any>?) {
    Handler(Looper.getMainLooper()).post {
      if (channel != null) {
        channel.invokeMethod(name, param)
      } else {
        Log.w(
          TAG,
          "Tried to call $name on closed channel"
        )
      }
    }
  }

  private val bluetoothCallback = object : BtEventCallback() {

    override fun onConnection(device: BluetoothDevice?, status: Int) {
      Log.e(TAG,"onConnection==>$status")
      if(status==StateCode.CONNECTION_OK){
        connectResult?.success(true)
        connectResult = null
      }else if(status==StateCode.CONNECTION_DISCONNECT){
        connectResult?.success(false)
        connectResult = null
    }
      if ((status == StateCode.CONNECTION_FAILED || status == StateCode.CONNECTION_DISCONNECT) && bluetoothHelper?.getConnectedDevice() != null) {
        bluetoothHelper?.disconnectDevice(bluetoothHelper?.getConnectedDevice())
      }
    }

    override fun onMandatoryUpgrade(device: BluetoothDevice?) {
      Log.e(TAG,"onMandatoryUpgrade==>")
    }
  }

  override fun onListen(arguments: Any?, events: EventChannel.EventSink?) {
    eventSink = events
  }

  override fun onCancel(arguments: Any?) {
    eventSink = null
  }
}
